﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using xn;
using System.Windows.Media.Imaging;
using System.Windows;
using System.Windows.Media;

namespace ActionRecognition
{
    public class Kinect
    {
        public Context context;
        public UserGenerator userGenerator;
        public DepthGenerator depthGenerator;
        public ImageGenerator imageGenerator;
        public ImageMetaData imageMetaData;
        public SkeletonCapability skeleton;
        public Thread updateThread;

        public List<User> userList;
        public bool keepUpdate = true;
        public delegate void UserUpdateEventHandler(object sender, UserUpdateEventArgs e);
        public event UserUpdateEventHandler userUpdateEventHandler;
        public event EventHandler StateChangedEventHandler;
        public State state;

        int frameNumber = 0;

        /// <summary>
        /// Constructor of Kinect
        /// </summary>
        /// <param name="configuration">Path to Kinect XML Configuration</param>
        public Kinect(String configuration)
        {
            context = new Context(configuration);

            userGenerator = new UserGenerator(context);
            userGenerator.NewUser += new UserGenerator.NewUserHandler(userGenerator_NewUser);
            userGenerator.LostUser += new UserGenerator.LostUserHandler(userGenerator_LostUser);
            userList = new List<User>();

            imageGenerator = new ImageGenerator(context);
            imageMetaData = new ImageMetaData();
            depthGenerator = new DepthGenerator(context);

            skeleton = userGenerator.GetSkeletonCap();
            skeleton.CalibrationStart += new SkeletonCapability.CalibrationStartHandler(skeleton_CalibrationStart);
            skeleton.CalibrationEnd += new SkeletonCapability.CalibrationEndHandler(skeleton_CalibrationEnd);
            skeleton.SetSkeletonProfile(SkeletonProfile.All);
            
            updateThread = new Thread(new ThreadStart(update));
            updateThread.Start();

            keepUpdate = true;
            state = State.Uncalibrated;
        }

        /// <summary>
        /// Update data from kinect, this is a thread function
        /// </summary>
        void update()
        {
            while (keepUpdate)
            {
                context.WaitAndUpdateAll();
                frameNumber++;
                foreach (User user in userList)
                {
                    if (user.isCalibrated)
                    {
                        for (int i = 0; i < user.listSkel.Count; i++)
                        {
                            skeleton.GetSkeletonJoint(user.id, user.listSkel[i].jointName, ref user.listSkel[i].transformation);
                        }
                    }
                }
                imageGenerator.GetMetaData(imageMetaData);
                userUpdateEventHandler(this, new UserUpdateEventArgs(frameNumber));
            }
            Console.WriteLine("Update thread was stopped");
        }

        void userGenerator_LostUser(ProductionNode node, uint id)
        {
            User user = userList.Find(u => u.id == id);
            userList.Remove(user);

            state = State.LostUser;
            StateChangedEventHandler(this, null);
            
            Console.WriteLine(String.Format("User baru dengan ID {0} hilang", id));
        }

        void userGenerator_NewUser(ProductionNode node, uint id)
        {
            userList.Add(new User(id));            
            skeleton.RequestCalibration(id,true);
            state = State.NewUser;
            StateChangedEventHandler(this, null);

            Console.WriteLine(String.Format("Terdeteksi user baru dengan ID : {0}", id));
        }

        void skeleton_CalibrationStart(ProductionNode node, uint id)
        {
            state = State.Calibrating;
            StateChangedEventHandler(this, null);
            Console.WriteLine(String.Format("Kalibrasi user {0} dimulai", id));
        }

        void skeleton_CalibrationEnd(ProductionNode node, uint id, bool success)
        {
            if (success)
            {
                userList.Where(u => u.id == id).First().isCalibrated = true;
                skeleton.StartTracking(id);
                state = State.Tracking;
            }
            else
            {
                skeleton.RequestCalibration(id, true);
                state = State.Uncalibrated;
            }
            StateChangedEventHandler(this, null);
            Console.WriteLine(String.Format("Kalibrasi user {0} selesai, status : {1}", id, success));
        }
    }

    public enum State {NewUser,Uncalibrated,PoseDetected,Calibrating,Calibrated,Tracking,LostUser};

    public class UserUpdateEventArgs : EventArgs
    {
        public int frame;
        public UserUpdateEventArgs(int _frame) : base() 
        {
            frame = _frame;
        }
    }
}
